|
|
|
|
|
Arrays are homogeneous structures. |
|
An array is something that you can stick a lot
of different things into, but all of them must be the same type. |
|
An array is a group of things that are together
because they are the same type not necessarily the same value. |
|
|
|
|
|
|
Let us consider an array of social security
numbers. Let picture of the disk represents each of the numbers: |
|
<data type> <array name>[<size>]; |
|
int SSN[5]; |
|
SSN[1] = 345; |
|
|
|
|
|
|
An array is a collection of variables of the
same type. Individual array elements are identified by an integer index. In
C the index begins at zero and is always written inside square brackets. |
|
|
|
int results[20]; |
|
int results_2d[20][5]; |
|
int results_3d[20][5][3]; |
|
|
|
|
|
var |
|
a: array[0..9] of integer; |
|
b: array[0..4, 0..9] of integer; |
|
int a[10]; |
|
float b[5][10]; |
|
|
|
Arrays in C always have subscripts ranging from
0 to declared size – 1. |
|
|
|
|
#include <iostream.h> |
|
int main() { |
|
int x, y, anarray[8][8];//declares an array
like a chessboard |
|
for(x=0; x<8; x++) { // sets the element
to |
|
for(y=0; y<8; y++) { // zero; after
the loop all |
|
anarray[x][y]=0; // elements == 0 |
|
} } |
|
for(x=0; x<8;x++) { |
|
for(y=0; y<8; y++) {
cout<<"anarray["<<x<<"]["<<y<<"]="<<anarray[x][y]<<"
"; |
|
} } |
|
return 0; |
|
} |
|
|
|
|
int add_array(int array[], int size) |
|
{ |
|
int i; |
|
int total = 0; |
|
|
|
for (i=0; i < size; i++) |
|
total += array[i]; |
|
|
|
return(total); |
|
} |
|
|
|
|
|
An array identifier is a constant pointer. |
|
Arrays are stored by rows. |
|
Let AnArray be a 2-dimensional array of type type. |
|
The type of AnArray is a pointer to a row. |
|
The type of *AnArray is a pointer to type. |
|
In the expression AnArray+i, i is scaled by the
size of the row. |
|
In the expression *AnArray+i, i is scaled by the
size of type. |
|
*AnArray = AnArray[0] |
|
**AnArray = *(AnArray[0]) = AnArray[0][0] |
|
AnArray[i] = *(AnArray+i) = *AnArray + i* number
of columns |
|
|
|
|
|
If AnArray is a single-dimensional array of type
type passed as parameter to a function, AnArray may be declared as |
|
type AnArray[]; |
|
The size of AnArray may be specified, but it is
neglected. |
|
type * AnArray; |
|
If AnArray is 2-dimensional array of type passed
as parameter to a function, AnArray may be declared as |
|
type AnArray[][size2]; |
|
And the specification of the constant size2 must
not be omitted. |
|
|
|
|
|
Pointers are data types. |
|
They store memory addresses of variables. |
|
|
|
|
|
|
The pointer is like any other variable except
that the value stored in it has a different interpretation: It is the address
of some memory location. |
|
Pointer is variable that holds a memory address |
|
|
|
|
ThePointer is defined as a variable of the type
pointer to integer, or simply, pointer. Assume value 5FA62 is stored in ThePointer.
This value is interpreted as a memory address. |
|
If the contents of memory location 5FA62 is 658,
then the value of ThePointer is 5FA62 and ThePointer points to the value 658. |
|
|
|
|
|
|
|
Sometimes the address stored in a pointer is not
important, so the following abbreviation can be used: |
|
|
|
|
Since the value stored in a pointer is a memory
address, the language has to provide a dereferencing mechanism: the
mechanism that can be used to access the value pointed to by the pointer. &px
has the value 654. |
|
& is called the address operator. |
|
|
|
|
|
Dereferencing refers to accessing the value that
a pointer points to. |
|
To do this “*” operator that interprets single
argument as an address and returns the contents of the address as the value
of the operation. Usage: |
|
*p |
|
EXAMPLE: |
|
int i = 25, *j; |
|
Here, j is a pointer to integer. Assume i and j
are allocated memory cells at addresses 5FA62 and 5FA70, respectively. This
means that the value of i is 25, &i is 5FA62 and the value of &j is
5FA70. |
|
|
|
|
The initial value of a variable, including a
pointer variable, is undetermined. Let assume that the initial value stored
in p is 5FA62: |
|
|
|
|
The initial value of a variable, including a
pointer variable, is undetermined. Let assume that the initial value stored
in p is 5FA62: |
|
|
|
|
|
|
|
|
|
|
|
#include <iostream.h> |
|
int main() |
|
{ |
|
int x; //A normal integer |
|
int *pointer; //A pointer to an integer |
|
pointer=&x; //Read it, |
|
//"pointer equals the
address of x“ |
|
cin>>x; //Reads in x |
|
cout<<*pointer; //Note the use of the
* to // output the actual number stored in x |
|
return 0; |
|
} |
|
|
|
|
var |
|
p: ^integer; |
|
first: ^student; |
|
struct student *first; |
|
|
|
|
type |
|
stuptr = ^node; |
|
var |
|
first:
stuptr; |
|
typedef |
|
struct student *stuptr; |
|
stuptr first; |
|
|
|
|
|
|
|
The special value NULL can be assigned to the
pointer to indicate that its value is defined but that it does not point to
anything. |
|
The NULL is usually equal to 0. |
|
A convenient way to set the initial value of a
pointer to NULL is in the definition itself: |
|
|
|
int *pi = NULL, *pj = NULL; |
|
|
|
|
|
In C++, the new and delete operators provide
build-in language support for dynamic memory allocation and deallocation. |
|
This feature has several benefits: |
|
Reduces common programmer errors: it is easy to
forget to multiply the number of objects being allocated by sizeof when
using malloc. |
|
Enhances source code clarity: generally, there
is no need to: (1) declare operator new and delete, (2) explicitly use
casts, or (3) explicitly check the return value. |
|
Improves run-time efficiency: (1) users can
redefine operator new and delete globally and also define then on a
per-class basis and (2) calls can be inlined. |
|
|
|
|
The keyword new is used to initialise pointers
with memory from free store (a section of memory available to all
programs). |
|
|
|
int *ptr = new int; |
|
|
|
It initialises ptr to point to a memory address
of size int (because variables have different sizes, number of bytes, this
is necessary). The memory that is pointed to becomes unavailable to other
programs. This means that the careful coder will free this memory at the end
of its usage. |
|
|
|
|
The delete operator frees up the memory
allocated through new. To do so, the syntax is as in the example.
delete
ptr;
After deleting a pointer, it can be a good idea to reset it to point to NULL. |
|
|
|
NULL is a standard compiler-defined statement
that sets the pointer to point to, literally, nothing. By doing this, you
minimise the potential for doing something foolish with the pointer. |
|
|
|
|
|
new returns a pointer to the allocated memory |
|
The storage duration of the new object is from
the point of creation until the operator delete destroys it by deallocating
its memory, or until the end of the program. |
|
By default, an allocation failure (such as
insufficient or fragmented heap memory) results in the predefined exception
bad_alloc being thrown. |
|
You should use the delete operator to remove all
memory which has been allocated by the new operator. Failure to free memory
can result in memory leaks. |
|
The delete operator offers dynamic storage
deallocation, deallocating a memory block allocated by a previous call to new. |
|
|
|
|
#include <iostream.h> |
|
void display(long double **); |
|
void de_allocate(long double **); |
|
int NumRows = 3; |
|
int NumCols = 5; |
|
void display(long double **data) { |
|
for
(int i = 0; i < m; i++) { |
|
for (int j = 0; j < n; j++) |
|
cout << data[i][j] << " "; |
|
cout << "\n" << endl; |
|
} |
|
} |
|
|
|
|
void de_allocate(long double **data) { |
|
for
(int i = 0; i < NumRows; i++) |
|
delete[] data[i]; // STEP 1: delete the columns |
|
delete[] data; // STEP 2:
delete the rows |
|
} |
|
|
|
void initialise(long double ** data){ |
|
for (int i = 0; i < NumRows; i++) |
|
for
(int j = 0; j < NumCols; j++) |
|
data[i][j] = i + j; // arbitrary
initialisation |
|
} |
|
|
|
|
int main(void) { |
|
long
double **data; |
|
data =
new long double*[NumRows]; // STEP 1: SET UP THE ROWS. |
|
for
(int j = 0; j < NumRows; j++) |
|
data[j] = new long double[NumCols]; |
|
// STEP 2: SET UP THE COLUMNS |
|
|
|
initialise(data); |
|
display(data); |
|
de_allocate(data); |
|
return
0; |
|
} |
|
|
|
|
One thing that arrays don't require that other
variables do, is a reference operator when you want to have a pointer to
the string. |
|
EXAMPLE: |
|
|
|
|
|
//As opposed to |
|
|
|
|
|
|
|
|
|
|
|
|
The array can be initialised using pointers as
follows. |
|
|
|
ArrayExample = new int[25]; |
|
which allows to access ArrayExample just as if
it were an array. Keep in mind that to use delete you must put [] between
delete and ArrayExample to tell it to free all 25 bytes of memory
allocated.
delete [] ArrayExample; |
|
|
|
|
|
The new routine reserves a block of memory of
the requested size in a pool of free memory called the heap. |
|
Two separate data storage areas are used by the
program during run time: the stack and the heap. |
|
The lifetime of data stored on the stack is
relative to the lifetime of the subroutine that the data are defined in. |
|
The lifetime of data stored in the heap starts
with the execution of the allocation procedure new and ends when the
deallocation procedure delete is called. |
|
|
|
|
Works with vectors |
|
A heap is a binary tree in which every node is
larger than the values associated with either child. A heap and a binary
tree, for that matter, can be very efficiently stored in a vector, by
placing the children of node i in positions 2 * i + 1 and 2 * i + 2. |
|
Using this encoding, the largest value in the
heap is always located in the initial position, and can therefore be very
efficiently retrieved. |
|
|
|
|
|
|
Works with vectors |
|
A heap is a binary tree in which every node is
larger than the values associated with either child. A heap and a binary
tree, for that matter, can be very efficiently stored in a vector, by
placing the children of node i in positions 2 * i + 1 and 2 * i + 2. |
|
Using this encoding, the largest value in the
heap is always located in the initial position, and can therefore be very
efficiently retrieved. |
|
|
|
|
|
|
A heap is a particular organization of elements
in a range between two random access iterators [a, b). Its two key
properties are: |
|
|
|
1. *a is the largest element in the range. |
|
2. *a may be removed by the pop_heap
algorithm, or a new element can be added by the push_heap |
|
algorithm, in O(logN) time. |
|
|
|
These properties make heaps useful as priority
queues. |
|
The heap algorithms use less than (operator<)
as the default comparison. In all of the algorithms, an alternate
comparison operator can be specified. |
|
|
|
|
Pointers are NOT integers. |
|
Always to test whether or not memory allocation
requests are successful. |
|
Only memory that has been allocated using new
(or the related routines) should be freed using delete operator. |
|
To pass a parameter by variable, specify this
parameter that is a pointer, use a pointer to pointer. |
|
Always cast the NULL pointer when it is passed
as an actual parameter. |
|
Do not declare pointer as a global variable. |
|
|
|
|
Allocation of memory The action of acquiring a
block of memory from the heap. |
|
Deallocation of memory The action of returning a
block of memory to the heap. |
|
Dereferencing of a pointer’s value The value
pointed to by a pointer. |
|
Heap A pool of free memory. Dynamic memory
requires allocate memory from this area. |
|
Pointer A variable whose value is interpreted as
a memory address. A pointer points to the value stored in the location at
that address. |
|
|
|
|
|
|
Structure type |
|
|
|
|
|
|
|
|
|
|
|
Structure variable |
|
|
|
|
|
|
Structure type |
|
|
|
|
|
|
|
|
|
|
|
Structure variable |
|
|
|
|
|
|
Structure type |
|
|
|
|
|
|
|
|
|
|
|
Structure variable |
|
|
|
|
|
|
Structure type |
|
|
|
|
|
|
|
|
|
|
|
Structure variable |
|
|
|
|
|
|
Structure type |
|
|
|
|
|
|
|
|
|
|
|
Structure variable |
|
|
|
|
|
|
A C structure variable may be initialised when
it is declared. |
|
|
|
|
A C structure variable may be initialised when
it is declared – e.g. |
|
|
|
|
|
|
Declaration of pointers to structures: |
|
|
|
|
|
|
|
|
|
This defines John as a structure and pJohn as a
pointer to a structure. |
|
pJohn variable initialisation: |
|
pJohn can be made to point to John: |
|
pJohn = &John; |
|
new can be used to allocation memory for pJohn: |
|
int *pJohn = new student; |
|
|
|
|
|
The first attempt to access the fields of the
structure John through pJohn can be: |
|
(*pJohn).StudentID; |
|
|
|
An alternative way: |
|
pJohn->StudentID; |
|
|
|
|
#include <iostream.h> |
|
struct example { int field1; int field2; }; |
|
int main() |
|
{ example Structure; |
|
example *PointerToStructure; |
|
Structure.field1=12; |
|
PointerToStructure =& Structure; //&
is necessary when dealing with
structures and using pointers to them |
|
cout<< PointerToStructure->field1; //The
-> acts somewhat like the * when used with pointers. It says, get
whatever is at that memory address Not "get what that memory address
is" |
|
return 0; |
|
} |
|
|
|
|
|
|
|
NOTE: Each of the big blocks is a struct (or
class) that has a pointer to another one. Remember that the pointer only
stores the memory location of something, it is not that thing, so the arrow
goes to the next one. At the end, there is nothing for the pointer to point
to, so it does not point to anything, it should be set to "NULL"
to prevent it from accidentally pointing to a totally arbitrary and random
location in memory (which is very bad). |
|
|
|
|
struct node { |
|
int value; |
|
node *next; |
|
}; |
|
int main() { |
|
node *root; This will be the unchanging 1st
node |
|
root=new node; Now root points to a node
struct |
|
root->next=NULL; //The node root points
to has its |
|
// next pointer set equal to NULL |
|
root->value=5; // By using the ->
operator, you |
|
// can modify the node |
|
return 0; //a struct (root in this case)
points to. |
|
} |
|
|
|
|
int main() { |
|
//This won't change, or
we would lose the list in memory |
|
//This will point to
each node as it traverses the list |
|
//Sets it to
actually point to something |
|
//Otherwise it
would not work well |
|
|
|
//The conductor
points to the first node |
|
|
|
|
|
// Creates a
node at the end of the list |
|
//Points to that
node |
|
//Prevents it
from going any further |
|
|
|
} |
|
|
|
|
conductor=root; |
|
// Makes sure there is a place to start |
|
if(conductor!=NULL) { |
|
while(conductor->next!=NULL) { |
|
cout<<conductor->x; |
|
conductor=conductor->next; |
|
} |
|
cout<<conductor->x; |
|
} |
|
|
|
|
|
|
|
|
Functions may return pointers to structures, and
they may have parameters that are pointers to structures. |
|
A function may return a structure, and
structures may be passed as parameters. |
|
|
|
|
TASK: The
program discussed shows 2 functions: *new() and new1 that create a new
object for the structure |
|
|
|
|
var |
|
student: record |
|
id: integer; |
|
name: packed array[1..10] of char; |
|
gpa: real |
|
end; |
|
struct |
|
{ int id; |
|
char
name[11]; |
|
float
gpa; |
|
} student; |
|
|
|
|
var |
|
borrower: record |
|
case boolean of |
|
false: (EBorr: emploee); |
|
true: (SBorr: student) |
|
end; |
|
union |
|
{
emploee EBorr; |
|
student SBorr; |
|
} borrower; |
|
|
|
|
var |
|
borrower: record |
|
case IsStudent of |
|
false: (EBorr: emploee); |
|
true: (SBorr: student) |
|
end; |
|
NO DIRECT EQUIVALENT IN C. THIS CAN BE HANDLED
BY CREATING A STRUCT WHICH CONTAINS THE TAG AND A UNION AS ITS FIELDS. (SEE
NEXT SLIDE) |
|
|
|
|
var |
|
borrower: record |
|
id: integer; |
|
name: packed array[1..10] of char; |
|
case boolean of |
|
false: (EBorr: emploee); |
|
true: (SBorr: student) |
|
end; |
|
struct |
|
{ int
id; |
|
char
name[11]; |
|
union |
|
{ emploee EBorr; |
|
student SBorr; |
|
}
Borr; |
|
} borrower; |
|
|
|
|
NO STANDARD PASCAL EQUIVALENT (BUT MANY DIALECTS
ALLOW THIS) |
|
A C variable may be initialised when it is
declared – e.g. |
|
int i = 3; |
|
char c = ‘A’; |
|
float a[3] = {1.0, 2.0, 3.0}; |
|
struct |
|
{ char name[11]; |
|
float
gpa; |
|
} student = {“Smith”, 4.0}; |
|
|
|
|
|
Structures are stored in blocks of consecutive
memory locations with padding if memory alignment is required. |
|
Structure assignments and passing a structure as
a parameter involves copying the entire structure. |
|
C does not permit self-referential structure
definitions: |
|
|
|
|
Attempts to access the fields of the structure John
through PointerJohn that is defined as |
|
PointerJohn = &John;. |
|
‘.’ has higher precedence than *, so the
expression |
|
|
|
would be interpreted as: |
|
|
|
The following expression is get out of the
problem: |
|
|
|
As an alternative notation that is equivalent to
the preceding expression is: |
|